#include "AIStateMachine.h"
#include "Entity.h"
#include "Physics.h"
#include <random>
#include "Planets.h"
#include "Enemy.h"
#include "CreateProjectileMessage.h"
#include "Player.h"
#include "../SGD Wrappers/SGD_GraphicsManager.h"
#include "FireGrappleMessage.h"
#include "GamePlayState.h"
#include "EntityManager.h"
#include "Planets.h"
#include "Shadowmancer.h"
#include "../SGD Wrappers/SGD_Event.h"

void AISM::SetTarget(Player*target)
{
	player = target;
}

void AISM::update(Enemy&en, float dt)
{
	EType type = en.GetEType();
	switch (type)
	{
	case shadisc:
	case shaflak:
		if (en.InRange() && en.GetState()!=State::standBy)
			en.SetState(track);
		else if (en.GetState() != State::standBy)
			en.SetState(State::move);
		break;
	case shike:
		if (en.InRange() && player->GetDestinationPlanet())
			en.SetState(track);
		else
		{
			en.SetSpike(false);
			en.SetState(State::move);
		}
		break;
	}

	switch (en.GetState())
	{
	case  State::move:
		Move(en, dt);
		break;
	case  track:
		Track(en, dt);
		break;
	case  standBy:
		break;
	}
}

void AISM::Move(Enemy&en, float dt)
{
	if (en.GetCurrentPlanet())
	{
		std::random_device rd; //create the random device seed
		std::mt19937 mt(rd()); //create the random engine and seed it with the device
		std::uniform_int_distribution<int> randRadius(1, 10);
		static int x;
		en.waited += dt;
		if (en.waited >= 5)
		{
			x = randRadius(mt);
			en.waited = 0;
		}
		if (x > 5)
		{
			OrbitClockwise(&en, en.GetCurrentPlanet());
			en.SetFacingLeft(false);
		}
		else
		{
			OrbitCounterClockwise(&en, en.GetCurrentPlanet());
			en.SetFacingLeft(true);
		}
	}
}

void AISM::Track(Enemy&en, float dt)
{
	EType type = en.GetEType();
	static float summonTimer = 5;
	switch (type)
	{
	case shabun:
		break;
	case  shadisc:
	{
		if (en.GetCurrentPlanet())
		{
			std::random_device rd; //create the random device seed
			std::mt19937 mt(rd()); //create the random engine and seed it with the device
			SGD::Point pcent = player->GetRect().ComputeCenter();
			SGD::Point hook = en.GetHookPos();
			static SGD::Point target;
			float min, max;
			if (player->GetDestinationPlanet())
			{
				if (pcent.x < hook.x)
				{
					min = pcent.x;
					max = hook.x;
				}
				else
				{
					min = hook.x;
					max = pcent.x;
				}
				std::uniform_real_distribution<float> randx(min, max);
				if (pcent.y < hook.y)
				{
					min = pcent.y;
					max = hook.y;
				}
				else
				{
					min = hook.y;
					max = pcent.y;
				}
				std::uniform_real_distribution<float> randy(min, max);
				en.waited += dt;
				if (en.waited >= 2)
				{
					target = SGD::Point{ randx(mt), randy(mt) };
				}
			}
			else
				target = pcent;

			SGD::Vector toTarget = en.GetRect().ComputeCenter() - target;
			SGD::Vector self = en.GetCurrentPlanet()->GetRect().ComputeCenter() - en.GetRect().ComputeCenter();
			self.Normalize();
			toTarget.Normalize();
			float dotprod = self.ComputeDotProduct(toTarget);
			if (fabs(dotprod) < 0.999f)
			{
				if (self.ComputeSteering(toTarget) < 0.0f)
				{
					OrbitCounterClockwise(&en, en.GetCurrentPlanet());
					en.SetFacingLeft(true);
				}
				else
				{
					OrbitClockwise(&en, en.GetCurrentPlanet());
					en.SetFacingLeft(false);
				}
			}
			else if (!player->GetDestinationPlanet() && !player->GetCurrentPlanet())
			{
				std::uniform_real_distribution<float> randx(.01f, .75f);
				float x = randx(mt);
				SGD::Point target = player->GetRect().ComputeCenter() + player->GetVelocity()*x;
				SGD::Vector jtarget = en.GetRect().ComputeCenter() - target;
				SGD::Vector self = en.GetCurrentPlanet()->GetRect().ComputeCenter() - en.GetRect().ComputeCenter();
				self.Normalize();
				jtarget.Normalize();
				float dotprod = self.ComputeDotProduct(jtarget);
				if (fabs(dotprod) < 0.9f)
				{
					if (self.ComputeSteering(toTarget) < 0.0f)
						OrbitCounterClockwise(&en, en.GetCurrentPlanet());
					else
						OrbitClockwise(&en, en.GetCurrentPlanet());
				}
				else
				{
					SGD::Vector zero = SGD::Vector{ 0, 0 };
					en.SetVelocity(zero);
					Jump(&en, en.GetCurrentPlanet(),200);
				}
				en.SetCurrentPlanet(nullptr);
			}
			else
			{
				SGD::Vector zero = SGD::Vector{ 0, 0 };
				en.SetVelocity(zero);
				if (en.GetFireTimer() <= 0.0f)
				{
					CreateProjectileMessage* msg = new CreateProjectileMessage(&en, &target);
					msg->QueueMessage();
					msg = nullptr;
					en.SetFireTimer(3.0f);
				}
			}
		}
	}
		break;
	case  shaflak:
	{
		SGD::Vector toTarget = en.GetRect().ComputeCenter() - player->GetRect().ComputeCenter();
		SGD::Vector self = en.GetCurrentPlanet()->GetRect().ComputeCenter() - en.GetRect().ComputeCenter();
		self.Normalize();
		toTarget.Normalize();
		float dotprod = self.ComputeDotProduct(toTarget);
		if (dotprod < 0.99f)
		{
			if (self.ComputeSteering(toTarget) < 0.0f)
			{
				OrbitCounterClockwise(&en, en.GetCurrentPlanet());
				en.SetFacingLeft(true);
			}
			else
			{
				OrbitClockwise(&en, en.GetCurrentPlanet());
				en.SetFacingLeft(false);
			}
		}
		else if (!en.Flipped())
		{
			SGD::Vector zero = SGD::Vector{ 0, 0 };
			en.SetVelocity(zero);
			if (en.GetFireTimer() <= 0.0f)
			{
				CreateProjectileMessage* msg = new CreateProjectileMessage(&en);
				msg->QueueMessage();
				msg = nullptr;
				en.SetFireTimer(3.0f);
			}
		}
		if (en.GetCurrentPlanet() == player->GetCurrentPlanet() && en.GetFireTimer() <= 0.0f && !en.Flipped())
		{
			SGD::Vector zero = SGD::Vector{ 0, 0 };
			en.SetVelocity(zero);
			Jump(player, &en);
			player->SetCurrentPlanet(nullptr);
			player->SetJumpTimer(.3f);
			//stomp
			//SGD::GraphicsManager::GetInstance()->DrawString("Stomp", { 200, 400 }, {});
			en.SetFireTimer(3.0f);
		}

	}
		break;
	case  shike:
	{
		if (en.GetCurrentPlanet() && player->GetDestinationPlanet() && en.GetCurrentPlanet() != player->GetDestinationPlanet())
		{
			SGD::Vector toTarget = en.GetRect().ComputeCenter() - player->GetDestinationPlanet()->GetRect().ComputeCenter();
			SGD::Vector self = en.GetCurrentPlanet()->GetRect().ComputeCenter() - en.GetRect().ComputeCenter();
			self.Normalize();
			toTarget.Normalize();
			float dotprod = self.ComputeDotProduct(toTarget);
			if (dotprod < 0.99f)
			{
				if (!en.Flipped())
				{
					if (self.ComputeSteering(toTarget) < 0.0f)
						OrbitCounterClockwise(&en, en.GetCurrentPlanet());
					else
						OrbitClockwise(&en, en.GetCurrentPlanet());
				}
			}
			else
			{
				SGD::Vector zero = SGD::Vector{ 0, 0 };
				en.SetVelocity(zero);
				Jump(&en, en.GetCurrentPlanet());
				en.SetCurrentPlanet(nullptr);
			}

		}
		else if (en.GetCurrentPlanet() == player->GetDestinationPlanet() && !en.Spiked())
		{
			if (player->GetDestinationPlanet())
			{
				SGD::Vector toTarget = en.GetHookPos() - en.GetRect().ComputeCenter();
				SGD::Vector self = en.GetRect().ComputeCenter() - en.GetCurrentPlanet()->GetRect().ComputeCenter();
				self.Normalize();
				toTarget.Normalize();
				float dotprod = self.ComputeDotProduct(toTarget);
				if (fabs(dotprod) < 0.999f)
				{
					if (self.ComputeSteering(toTarget) < 0.0f)
						OrbitCounterClockwise(&en, en.GetCurrentPlanet());
					else
						OrbitClockwise(&en, en.GetCurrentPlanet());
				}
				else
				{
					//disable anchor point
					SGD::Vector zero = SGD::Vector{ 0, 0 };
					en.SetVelocity(zero);
					//player->SetDestinationPlanet(nullptr);
					SGD::Event ev = { "GRAPPLEDETACH" };
					ev.SendEventNow(nullptr);
					en.SetSpike();
				}
			}
		}
		else if (en.Spiked() && en.GetFireTimer() <= 0.0f)
		{
			CreateProjectileMessage* msg = new CreateProjectileMessage(&en);
			msg->QueueMessage();
			msg = nullptr;
			en.SetFireTimer(3.0f);
		}
		else if (!en.Spiked())
			Move(en, dt);
	}
		break;
	case  shatent:
	{
		en.waited += dt;
		std::random_device rd; //create the random device seed
		std::mt19937 mt(rd()); //create the random engine and seed it with the device
		std::uniform_int_distribution<int> randAttack(1, 2);
		if (en.waited >= 5)
		{
			static int x = randAttack(mt);
			if (en.InRange())
			{	
				switch (x)
				{
				case 1: //grab and throw
					player->SetDestinationPlanet(nullptr);
					player->SetCurrentPlanet(nullptr);
					Jump(player, &en,500);
					break;
				case 2: // slap
					player->SetDestinationPlanet(nullptr);
					player->SetCurrentPlanet(nullptr);
					Jump(player, &en,200);
					break;
				}
			}
			en.waited = 0;
		}
	}
		break;
	case  shamancer:
	{
		summonTimer -= dt;
		EntityManager*em = GamePlayState::GetInstance()->GetEntityManager();
		Shadowmancer*sm = dynamic_cast<Shadowmancer*>(&en);
		//if on same planet
		if (player->GetCurrentPlanet() && en.GetCurrentPlanet() && player->GetCurrentPlanet() == en.GetCurrentPlanet())
		{
			SGD::Vector toTarget = player->GetCurrentPlanet()->GetRect().ComputeCenter() - player->GetRect().ComputeCenter();
			SGD::Vector self = en.GetCurrentPlanet()->GetRect().ComputeCenter() - en.GetRect().ComputeCenter();
			self.Normalize();
			toTarget.Normalize();
			float dotprod = self.ComputeDotProduct(toTarget);
			if (dotprod < 0.99f)
			{
				if (self.ComputeSteering(toTarget) < 0.0f)
					OrbitCounterClockwise(&en, en.GetCurrentPlanet());
				else
					OrbitClockwise(&en, en.GetCurrentPlanet());
			}
		}
		//if on diff planet
		else if (player->GetCurrentPlanet() && en.GetCurrentPlanet() && player->GetCurrentPlanet() != en.GetCurrentPlanet())
		{
			SGD::Vector toTarget = en.GetRect().ComputeCenter() - player->GetCurrentPlanet()->GetRect().ComputeCenter();
			SGD::Vector self = en.GetCurrentPlanet()->GetRect().ComputeCenter() - en.GetRect().ComputeCenter();
			self.Normalize();
			toTarget.Normalize();
			float dotprod = self.ComputeDotProduct(toTarget);
			if (dotprod < 0.99f)
			{
				if (self.ComputeSteering(toTarget) < 0.0f)
					OrbitCounterClockwise(&en, en.GetCurrentPlanet());
				else
					OrbitClockwise(&en, en.GetCurrentPlanet());
			}
			else
			{
				SGD::Vector zero = SGD::Vector{ 0, 0 };
				en.SetVelocity(zero);
				Jump(&en, en.GetCurrentPlanet());
				en.SetCurrentPlanet(nullptr);
			}
		}
		// max grapple length is 400
		//if player jumping
		else if (!player->GetDestinationPlanet() && !player->GetCurrentPlanet())
		{
			if (sm->attached)
			{
				sm->attached = false;
			}
			else
			{
				
			}
		}
		//if player grappling
		else if (player->GetDestinationPlanet() && !player->GetCurrentPlanet() &&  !en.GetDestinationPlanet())
		{
			SGD::Vector zero = SGD::Vector{ 0, 0 };
			en.SetVelocity(zero);
			for (size_t i = 0; i < em->Size(0); i++)
			{
				Entity*help = (Entity*)em->Save(0, i);
				SGD::Vector toTarget = en.GetRect().ComputeCenter() - help->GetRect().ComputeCenter();
				if (toTarget.ComputeLength() < 400 && !sm->attached && help != sm->GetCurrentPlanet())
				{
					FireGrappleMessage*fm = new FireGrappleMessage(&en, (Planets*)help);
					fm->QueueMessage();
					fm = nullptr;
					sm->attached = true;
				}
			}
		}
		else if (en.GetDestinationPlanet())
		{
			SGD::Vector toTarget = en.GetRect().ComputeCenter() - player->GetRect().ComputeCenter();
			SGD::Vector self = en.GetDestinationPlanet()->GetRect().ComputeCenter() - en.GetRect().ComputeCenter();
			float dist = self.ComputeLength();
			self.Normalize();
			toTarget.Normalize();
			float dotprod = self.ComputeDotProduct(toTarget);
			if (dotprod < 0.99f)
			{
				if (self.ComputeSteering(toTarget) < 0.0f)
					OrbitCounterClockwise(&en, en.GetDestinationPlanet());
				else
					OrbitClockwise(&en, en.GetDestinationPlanet());
			}
			else
			{
				static float pullspeed = 0;
				SGD::Vector pullforce = en.GetDestinationPlanet()->GetRect().ComputeCenter() - en.GetRect().ComputeCenter();
				float distance = pullforce.ComputeLength();
				pullforce.Normalize(); 
				if (self.ComputeSteering(toTarget) < 0.0f)
				{
					pullspeed -= dt * 30;
					if (pullspeed < -30)
						pullspeed = -30;
					en.SetVelocity(en.GetVelocity() + pullforce * pullspeed);
				}
				else
				{
					pullspeed += dt * 30;
					if (pullspeed > 30)
						pullspeed = 30;
					en.SetVelocity(en.GetVelocity() + pullforce * pullspeed);
				}
				if (dist >= 400)
				{
					for (size_t i = 0; i < em->Size(0); i++)
					{
						Entity*help = (Entity*)em->Save(0, i);
						SGD::Vector toTarget = en.GetRect().ComputeCenter() - help->GetRect().ComputeCenter();
						if (toTarget.ComputeLength() < 400 && !sm->attached && help != sm->GetCurrentPlanet())
						{
							FireGrappleMessage*fm = new FireGrappleMessage(&en, (Planets*)help);
							fm->QueueMessage();
							fm = nullptr;
							sm->attached = true;
						}
					}
				}
			}
		}
		//if summontimer up
		if (summonTimer < 0)
		{
			for (size_t i = 0; i < em->Size(0); i++)
			{
				Entity*help = (Entity*)em->Save(0, i);
				SGD::Vector toTarget = en.GetRect().ComputeCenter() - help->GetRect().ComputeCenter();
				if (toTarget.ComputeLength() < en.GetRange())
				{
					Enemy * newenemyobject = GamePlayState::GetInstance()->CreateEnemy();
					int positionangle = rand() % 361;
					SGD::Point position;
					position.x = float(cos(positionangle) * help->GetSize().width / 2 + help->GetRect().ComputeCenter().x);
					position.y = float(sin(positionangle) * help->GetSize().width / 2 + help->GetRect().ComputeCenter().y);
					newenemyobject->SetPosition(position);
					newenemyobject->SetCurrentPlanet(dynamic_cast<Planets*>(help));
					em->AddEntity(newenemyobject, 1);
					newenemyobject->Release();
				}
			}
			summonTimer = 5;
		}
	}
		break;
	}
}

